package com.ruoyi.system.service.impl;

import java.util.*;
import java.util.concurrent.TimeUnit;

import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.system.config.RedissonConfig;
import com.ruoyi.system.domain.CustomerCoupon;
import com.ruoyi.system.mapper.CustomerCouponMapper;
import io.jsonwebtoken.Jwts;

import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.ruoyi.system.mapper.CouponInfoMapper;
import com.ruoyi.system.domain.CouponInfo;
import com.ruoyi.system.service.CouponInfoService;

import javax.annotation.Resource;

/**
 * 优惠卷管理Service业务层处理
 *
 * @author ruoyi
 * @date 2024-10-19
 */
@Service
public class CouponInfoServiceImpl implements CouponInfoService
{

    //开放优惠券的key
    private final String STARTKEY = "startCouponsKey:";
    //缓存中存储所有优惠券信息的KEY
    private final String COUPONSALL_KEY = "coupons-all";
    //缓存中存储优惠券详细信息的KEY
    private final String COUPONSINFO_KEY = "coupons-info:";
    //redis锁的KEY
    private final String LOCK_KEY = "lock-coupons:";
    private final String LOCK_START_KEY = "lock-start-coupons:";

    @Autowired
    private CouponInfoMapper couponInfoMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

//    @Resource
//    private Redisson redisson;

    @Autowired
    private RedissonConfig redissonConfig;

    @Autowired
    private CustomerCouponMapper customerCouponMapper;

    /**
     * 查询优惠卷管理
     *
     * @param couponInfoId 优惠卷管理主键
     * @return 优惠卷管理
     */
    @Override
    public CouponInfo selectById(Long couponInfoId)
    {
        return couponInfoMapper.selectById(couponInfoId);
    }

    /**
     * 查询优惠卷管理列表
     *
     * @param couponInfo 优惠卷管理
     * @return 优惠卷管理
     */
    @Override
    public List<CouponInfo> select(CouponInfo couponInfo)
    {
        return  couponInfoMapper.select(couponInfo);
    }

    /**
     * 新增优惠卷管理
     *
     * @param couponInfo 优惠卷管理
     * @return 结果
     */
    @Override
    public int insert(CouponInfo couponInfo)
    {

        return couponInfoMapper.insert(couponInfo);

    }

    /**
     * 修改优惠卷管理
     *
     * @param couponInfo 优惠卷管理
     * @return 结果
     */
    @Override
    public int update(CouponInfo couponInfo)
    {

        return couponInfoMapper.update(couponInfo);
    }

    /**
     * 批量删除优惠卷管理
     *
     * @param couponInfoIds 需要删除的优惠卷管理主键
     * @return 结果
     */
    @Override
    public int deleteByIds(Long[] couponInfoIds)
    {
        return couponInfoMapper.deleteByIds(couponInfoIds);
    }

    /**
     * 删除优惠卷管理信息
     *
     * @param couponInfoId 优惠卷管理主键
     * @return 结果
     */
    @Override
    public int deleteById(Long couponInfoId)
    {

        return couponInfoMapper.deleteById(couponInfoId);
    }

    /**
     * @description: //TODO 发放优惠券 开启活动
     * @param: [couponInfoId]
     * @return: AjaxResult
     **/
    @Override
    public AjaxResult startCoupons(Long couponInfoId) {
        //1--查询发送优惠券的信息
        CouponInfo couponInfo = couponInfoMapper.selectCouponInfoByCouponInfoId(couponInfoId);
        if (couponInfo == null) {
            return AjaxResult.error("未查到优惠券信息");
        }

        //开始做发放优惠券的前期工作
        //判断活动是否开始
        if (couponInfo.getTakeEffectTime().after(new Date())) {
            return AjaxResult.error("活动未开始");
        }
        //判断活动是否结束
        if (couponInfo.getExpireTime().before(new Date())) {
            return AjaxResult.error("获取已结束");
        }
        /* 修改优惠券的开放状态
         * 0 待发放
         * 1 正在发放
         * 2 停止发放
         * 3 库存不足
         */
        int updateResult = couponInfoMapper.startCoupons(couponInfoId, 1);
        if (updateResult <= 0) {
            return AjaxResult.error("发放优惠券失败了");
        }

        //如果优惠券的额数量小于0不进行存储
        String couponKey = STARTKEY + couponInfoId;
        if (couponInfo.getReleaseNum() <= 0) {

            //修改优惠券的状态为库存不足
            couponInfoMapper.startCoupons(couponInfoId,3);

            return AjaxResult.error("优惠券库存不足");
        }

        //存入优惠券的数量
        HashOperations<String, Object, Object> opsForHash = stringRedisTemplate.opsForHash();
        Map map = new HashMap();
        map.put("count",String.valueOf(couponInfo.getReleaseNum()));
        map.put("name",couponInfo.getName());
        map.put("number",String.valueOf(couponInfo.getCouponInfoId()));
        //将优惠券信息存储到redis中
        opsForHash.putAll(couponKey, map);

        //将活动开启的优惠券信息存储到redis中
        stringRedisTemplate.delete(COUPONSALL_KEY);
        return AjaxResult.success("活动开启");
    }

    /**
     * @description: //TODO 关闭发送优惠券
     * @param: [couponInfoId]
     * @return: AjaxResult
     **/
    @Override
    public AjaxResult closeCoupons(Long couponInfoId) {

        //查询发送优惠券的信息
        int updateResult = couponInfoMapper.startCoupons(couponInfoId, 2);

        //判断优惠券是否开启
        if (updateResult > 0) {

            //获取要开放的优惠券的key
            String couponKey = STARTKEY + couponInfoId;
            //查询redis优惠券信息
            Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(couponKey);
            //获取优惠券的库存
            Long count = Long.valueOf(String.valueOf(entries.get("count")));
            //将缓存的优惠券库存信息重新同步在数据库中
            couponInfoMapper.updateCouponInfoById(couponInfoId, count);
            //删除redis中的优惠券信息
            stringRedisTemplate.delete(couponKey);
            //删除redis中所有优惠券的key
            stringRedisTemplate.delete(COUPONSALL_KEY);
            return AjaxResult.success("活动关闭");
        }
        return AjaxResult.error("优惠券不存在");
    }

    /***
     * @description //TODO 领取优惠券
     * @param couponId
     * @param token
     * @return com.ruoyi.common.core.web.domain.AjaxResult
     **/
    @Override
    public AjaxResult getCoupons(Long couponId, String token) {
        //解析token
        //Integer userId = Jwts.parser().setSigningKey("taxi").parseClaimsJws(token).getBody().get("userId", Integer.class);

        //这里自拟了一个微信用户opid {"session_key":"Q/QywcJkAeGp78S0VMO6XQ==","openid":"odh6Y7d1082eqZYHI7eK2rrwxpgM"}
        String userId = "odh6Y7d1082eqZYHI7eK2rrwxpgM24";

        //获取优惠券的key
        String couponKey = STARTKEY + couponId;

        // 获取与 couponKey 关联的所有哈希表条目
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(couponKey);

        //判断redis中是否存在优惠券
        if (entries == null && entries.size() <= 0) {
            //缓存中不存在，需要查询数据库
            CouponInfo couponInfo = couponInfoMapper.selectCouponInfoByCouponInfoId(couponId);
            if (couponInfo != null) {
                //数据库也不存在的情况下
                String nullKey = "couponsNull:" + couponId;
                stringRedisTemplate.opsForHash().put(nullKey, "不存在的优惠券Id", couponId + "");
                //设置缓存数据的过期时间
                stringRedisTemplate.opsForHash().getOperations().expire(nullKey, 1, TimeUnit.MINUTES);
                return AjaxResult.error("优惠券不存在");
            }
        }

        // 获取 redisson 锁 防止并发
        RLock lock = redissonConfig.redissonClient().getLock("coupons-lock:" + couponId); // 这里需要优化，防止并发
        try {
            boolean lockFlag = lock.tryLock(1, 10, TimeUnit.SECONDS);
            //上锁失败
            if (!lockFlag) {
                return AjaxResult.error("优惠券已被领取，手速有点慢了");
            }

            //判断用户是否已经领取过该优惠券
            String couponsSetKey = "couponsSet:" + couponKey;
            Set<String> couponsSet = stringRedisTemplate.opsForSet().members(couponsSetKey);
            if (couponsSet.size() > 0 && couponsSet != null) {
                boolean userFlag = couponsSet.contains(String.valueOf(userId));
                if (userFlag) {
                    return AjaxResult.error("已经领取过该优惠券了，请不要贪心哦");
                }
            }

            //获取redis中的数量
            long count = Integer.valueOf(String.valueOf(entries.get("count")));
            if (count <= 0) {

                return AjaxResult.error("库存不足了");
            }
            //更新优惠券的领取数量
            Long couponCount = stringRedisTemplate.opsForHash().increment(couponKey, "count", -1);

            //将获取的优惠券放入数据库
            CustomerCoupon customerCoupon = CustomerCoupon.builder()
                    .couponInfoId(couponId)
                    .userId(userId)
                    .receiveTime(new Date())
                    .status(Long.valueOf(0))
                    .build();
            int saveResult = customerCouponMapper.insertCustomerCoupon(customerCoupon);

            //领取成功，向缓存中添加领取记录
            stringRedisTemplate.opsForSet().add(couponsSetKey, "userId", String.valueOf(userId));
            if (saveResult > 0) {
                /*
                 * 领取成功，保证redis数据和数据库数据一致
                 * 使用乐观锁防止超卖问题
                 * 修改优惠券数据之后，保证mysql和redis数据一致
                 * */
                couponInfoMapper.updateCouponInfoById(couponId, couponCount);// 领取成功 修改优惠券数量
                stringRedisTemplate.delete(COUPONSALL_KEY);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            //释放锁
            lock.unlock();
        }

        return AjaxResult.success("领取成功");
    }

    /**
     * //todo 前端抢券的列表
     * @return
     */
    @Override
    public AjaxResult couponsList() {
        List<CouponInfo> couponInfoList =  couponInfoMapper.couponsList();
        return AjaxResult.success(couponInfoList);
    }

    /**
     * @description: //TODO 设置setnx锁
     * @param: [key]
     * @return: boolean
     **/
    private boolean tryLock(String key) {
        // 将键与值关联起来 如果key不存在，那么就设置过期时间为10秒
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return flag;
    }

    /**
     * @description: //TODO 关闭锁
     * @param: [key]
     * @return: void
     **/
    private void unLock(String key) {
        stringRedisTemplate.delete(key);
    }

}
